﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections.Generic;

/// <summary>
/// Summary description for ProfileService
/// </summary>
public class ProfileServiceImpl : IProfileService {
	public ProfileServiceImpl() {}

	public User authenticate(String email, String password) {
		User result = null;

		IProfileDAO profileDAO = AbstractDAOFactory.getInstance(AbstractDAOFactory.Type.MySQL).createProfileDAO();
		UserSearchCriteria userCriteria = new UserSearchCriteria();
		userCriteria.Emails = new List<string>();
		userCriteria.Emails.Add(email);
		userCriteria.PopulatePassword = true;
		List<User> users = profileDAO.readAccounts(userCriteria);
		if (users.Count > 0) {
			result = users[0];

			//If password does not match, return null
			if (!result.Enabled) {
				BusinessException be = new BusinessException();
				be.addMessage(new Message(Message.Type.WARNING, "Too many failed login attempts; account is locked"));
				throw be;
			} else if (!result.Password.Equals(password)) {
				//Increment failure counter
				if (FailedAuthenticationManager.GetInstance().increment(result)) {
					this.disableAccount(result);
				}

				result = null;
			} else { //User is authenticated; query schedules and destinations
				//Reset failed login counter
				FailedAuthenticationManager.GetInstance().reset(result);

				result.Destinations = new List<Destination>();
				IDestinationDAO destinationDAO = AbstractDAOFactory.getInstance(AbstractDAOFactory.Type.MySQL).createDestinationDAO();

				//First query user-specific destinations
				DestinationSearchCriteria destinationCriteria = new DestinationSearchCriteria();
				destinationCriteria.Global = false;
				destinationCriteria.Users = new Dictionary<int, User>();
				destinationCriteria.Users.Add(result.Id, result);
				result.Destinations.AddRange(destinationDAO.readDestinations(destinationCriteria));

				//Now query global destinations
				destinationCriteria.Global = true;
				destinationCriteria.Users = null;
				result.Destinations.AddRange(destinationDAO.readDestinations(destinationCriteria));

				//Populate schedules
				ScheduleSearchCriteria scheduleCriteria = new ScheduleSearchCriteria();
				scheduleCriteria.Users[result.Id] = result;
				IScheduleDAO scheduleDAO = AbstractDAOFactory.getInstance(AbstractDAOFactory.Type.MySQL).createScheduleDAO();
				scheduleDAO.readSchedules(scheduleCriteria);
			}
		}

		return result;
	}

	public void saveAccount(User user) {
		BusinessException exception = new BusinessException();

		IProfileDAO profileDAO = AbstractDAOFactory.getInstance(AbstractDAOFactory.Type.MySQL).createProfileDAO();
		
		//Check to ensure e-mail address is populated
		if ((user.Email == null) || (user.Email.Length == 0)) {
			exception.addMessage(new Message(Message.Type.ERROR, "E-mail address is required"));
		} else { //Only need to check for duplicatie e-mails if the user entered one
			//Check to make sure e-mail address isn't already is use if this is a new user account
			if (user.Id < 1) {
				UserSearchCriteria userCriteria = new UserSearchCriteria();
				userCriteria.Emails = new List<string>();
				userCriteria.Emails.Add(user.Email);
				List<User> users = profileDAO.readAccounts(userCriteria);
				if (users.Count > 0) {
					exception.addMessage(new Message(Message.Type.ERROR, user.Email + " is already registered, please use a different e-mail address"));
				}
			}
		}

		//Check to pet security answer is populated
		if ((user.SecurityAnswer_Pet == null) || (user.SecurityAnswer_Pet.Length == 0)) {
			exception.addMessage(new Message(Message.Type.ERROR, "Security answer to pet question is required"));
		}

		if (exception.Messages.Count > 0) {
			throw exception;
		} else {
			try {
				user.Enabled = true;
				profileDAO.saveAccount(user);
			} catch (PersistenceException e) {
				throw new ServiceException("Failed to reenable user account for user " + user.Id, e);
			}
		}
	}

	public void disableAccount(User user) {
		BusinessException exception = new BusinessException();

		if (exception.Messages.Count > 0) {
			throw exception;
		} else {
			try {
				IProfileDAO profileDAO = AbstractDAOFactory.getInstance(AbstractDAOFactory.Type.MySQL).createProfileDAO();
				user.Enabled = false;
				profileDAO.saveAccount(user);
			} catch (PersistenceException e) {
				throw new ServiceException("Failed to disable user account for user " + user.Id, e);
			}
		}
	}

	public void reenableAccount(string email, String securityAnswer_Pet) {
		//Look up user object
		IProfileDAO profileDAO = AbstractDAOFactory.getInstance(AbstractDAOFactory.Type.MySQL).createProfileDAO();
		UserSearchCriteria criteria = new UserSearchCriteria();
		criteria.Emails.Add(email);
		List<User> users = profileDAO.readAccounts(criteria);

		BusinessException exception = new BusinessException();

		if (users.Count > 0) {
			User user = users[0];

			//Check to ensure user answered security questions
			if (!user.SecurityAnswer_Pet.ToUpper().Equals(securityAnswer_Pet.ToUpper())) {
				exception.addMessage(new Message(Message.Type.ERROR, "Answers to security questions are incorrect"));
			}

			if (exception.Messages.Count < 1) {
				try {
					//Reenable in DB
					user.Enabled = true;
					profileDAO.saveAccount(user);

					//Reset counter
					FailedAuthenticationManager.GetInstance().reset(user);
				} catch (PersistenceException e) {
					throw new ServiceException("Failed to reenable user account for user " + user.Id, e);
				}
			}
		} else {
			exception.addMessage(new Message(Message.Type.ERROR, email + " not found"));
		}

		if (exception.Messages.Count > 0) {
			throw exception;
		}
	}

	public List<User> findAccounts(UserSearchCriteria userCriteria) {
		List<User> result = null;
		try {
			IProfileDAO profileDAO = AbstractDAOFactory.getInstance(AbstractDAOFactory.Type.MySQL).createProfileDAO();
			result = profileDAO.readAccounts(userCriteria);
		} catch (PersistenceException e) {
			throw new ServiceException("Failed to find user accounts", e);
		}
		return result;
	}
}
